#include "keyboard.h"

#include "global.h"
#include "interrupt.h"
#include "io.h"
#include "ioqueue.h"
#include "print.h"

#define OUTPUT_REGISTER_PORT 0x60
// 定义控制字符
#define esc '\033'
#define backsapce '\b'
#define tab '\t'
#define enter '\r'
#define delete '\0177'

// 定义控制字符对应的按键，即控制按键
#define invisible_char 0
#define ctrl_l_char invisible_char
#define ctrl_r_char invisible_char
#define shift_l_char invisible_char
#define shift_r_char invisible_char
#define alt_l_char invisible_char
#define alt_r_char invisible_char
#define capslock_char invisible_char

static char keymap[][2] = {
    {0, 0},                         // 0x00
    {esc, esc},                     // 0x01
    {'1', '!'},                     // 0x02
    {'2', '@'},                     // 0x03
    {'3', '#'},                     // 0x04
    {'4', '$'},                     // 0x05
    {'5', '%'},                     // 0x06
    {'6', '^'},                     // 0x07
    {'7', '&'},                     // 0x08
    {'8', '*'},                     // 0x09
    {'9', '('},                     // 0x0A
    {'0', ')'},                     // 0x0B
    {'-', '_'},                     // 0x0C
    {'=', '+'},                     // 0x0D
    {backsapce, backsapce},         // 0x0E
    {tab, tab},                     // 0x0F
    {'q', 'Q'},                     // 0x10
    {'w', 'W'},                     // 0x11
    {'e', 'E'},                     // 0x12
    {'r', 'R'},                     // 0x13
    {'t', 'T'},                     // 0x14
    {'y', 'Y'},                     // 0x15
    {'u', 'U'},                     // 0x16
    {'i', 'I'},                     // 0x17
    {'o', 'O'},                     // 0x18
    {'p', 'P'},                     // 0x19
    {'[', '{'},                     // 0x1A
    {']', '}'},                     // 0x1B
    {enter, enter},                 // 0x1C
    {ctrl_l_char, ctrl_l_char},     // 0x1D
    {'a', 'A'},                     // 0x1E
    {'s', 'S'},                     // 0x1F
    {'d', 'D'},                     // 0x20
    {'f', 'F'},                     // 0x21
    {'g', 'G'},                     // 0x22
    {'h', 'H'},                     // 0x23
    {'j', 'J'},                     // 0x24
    {'k', 'K'},                     // 0x25
    {'l', 'L'},                     // 0x26
    {';', ':'},                     // 0x27
    {'\'', '"'},                    // 0x28
    {'`', '~'},                     // 0x29
    {shift_l_char, shift_l_char},   // 0x2A
    {'\\', '|'},                    // 0x2B
    {'z', 'Z'},                     // 0x2C
    {'x', 'X'},                     // 0x2D
    {'c', 'C'},                     // 0x2E
    {'v', 'V'},                     // 0x2F
    {'b', 'B'},                     // 0x30
    {'n', 'N'},                     // 0x31
    {'m', 'M'},                     // 0x32
    {',', '<'},                     // 0x33
    {'.', '>'},                     // 0x34
    {'/', '?'},                     // 0x35
    {shift_r_char, shift_r_char},   // 0x36
    {'*', '*'},                     // 0x37
    {alt_l_char, alt_l_char},       // 0x38
    {' ', ' '},                     // 0x39
    {capslock_char, capslock_char}, // 0x3A
};

static bool is_extend_scancode = false;
static bool shift_pressed = false;
static bool ctrl_pressed = false;
static bool alt_pressed = false;
static bool capslock_pressed = false;

struct ioqueue kbd_buf;

static void interrupt_keyboard_handler();

void keyboard_init()
{
  put_str("keyboard init start\r\n");
  register_handler(0x21, interrupt_keyboard_handler);
  ioqueue_init(&kbd_buf);
  put_str("keyboard init done\r\n");
}

static void interrupt_keyboard_handler()
{
  uint16_t scancode = inb(OUTPUT_REGISTER_PORT);
  if (scancode == 0xe1)
  {
    // is extend scancode
    is_extend_scancode = true;
    return;
  }

  if (is_extend_scancode)
  {
    is_extend_scancode = false;
    // combine into double-byte code
    scancode += 0xe100;
  }

  // shift
  if (scancode==0x2a || scancode ==0xaa|| scancode ==0x36|| scancode ==0xb6)
  {
    shift_pressed = !shift_pressed;
    return;
  }

  // ctrl
  if (!(scancode ^ 0x1d)||!(scancode ^ 0x9d))
  {
    ctrl_pressed = !ctrl_pressed;
    return;
  }

  // alt
  if (!(scancode ^ 0x38)||!(scancode ^ 0xb8))
  {
    alt_pressed = !alt_pressed;
    return;
  }

  // capslock
  if (scancode == 0x3a)
  {
    capslock_pressed = !capslock_pressed;
    return;
  }

  if ((scancode & 0x80)==0x80)
  {
    // is break code
    return;
  }

  uint8_t index = (scancode &= 0xff);
  char    cur_char = keymap[index][shift_pressed ^ capslock_pressed];

  if (!ioqueue_is_full(&kbd_buf) && cur_char)
  {
    // TODO: delete line below after shell is finished
    put_char(cur_char);

    ioqueue_putchar(&kbd_buf, cur_char);
  }
}
